package com.facebook.app.client.presenter;

import java.util.List;

import com.facebook.app.client.FacebookServiceAsync;
import com.facebook.app.client.event.ActionFailedEvent;
import com.facebook.app.client.event.ReadyForClusteringEvent;
import com.facebook.app.shared.clustering.Clusterable;
import com.facebook.app.shared.facebookData.FacebookFriend;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.widgetideas.client.ProgressBar;

public class ClusterStrategyChosenPresenter implements Presenter {
	
	public static final int NUMBER_OF_IMPORTED_FRIENDS_PER_REQUEST = 8;
	public static final int MAX_NUMBER_OF_CONCURRENTLY_RUNNING_REQUESTS = 6;

	public interface Display {
		Panel getStatusPanel();
		Label getStatusLabel();
		ProgressBar getProgressBar();
		Image getLoadingImage();
		Widget asWidget();
	}

	private final FacebookServiceAsync rpcService;
	private final HandlerManager eventBus;
	private final Display view;
	private final String accessToken;
	
//	private HasWidgets container;
	
	private int currentFriendIndex = 0;
	private int currentNumberOfActiveRequests = 0;
	
	private double currentProgress;

	public ClusterStrategyChosenPresenter(FacebookServiceAsync rpcService,
			HandlerManager eventBus, Display view, String accessToken) {
		this.rpcService = rpcService;
		this.eventBus = eventBus;
		this.view = view;
		this.accessToken = accessToken;
	}
	
	@Override
	public void go(final HasWidgets container) {
//		this.container = container;
		container.clear();
		container.add(view.asWidget());
		
		deleteDataFromTheStore();
	}
	
	private void deleteDataFromTheStore() {
		
		view.getStatusLabel().setText("Deleting old data");
		
		rpcService.deleteDataFromTheStore(new AsyncCallback<Void>() {
			@Override
			public void onSuccess(Void result) {
				importFriends();
			}
			
			@Override
			public void onFailure(Throwable caught) {
				importFriends();
			}
		});
	}
	
	private void importFriends() {
		
		view.getStatusLabel().setText("Importing friends");
		
		rpcService.importFriends(accessToken, new AsyncCallback<FacebookFriend[]>() {
			@Override
			public void onSuccess(FacebookFriend[] allFriends) {
				importLikesForFriends(allFriends);
			}
			
			@Override
			public void onFailure(Throwable caught) {
				eventBus.fireEvent(new ActionFailedEvent("Friend import failed!"));
			}
		});
	}
	
	private void importLikesForFriends(FacebookFriend[] allFriends) {
		
		view.getStatusLabel().setText("Importing likes");
		view.getProgressBar().setMaxProgress(allFriends.length);
		view.getProgressBar().setProgress(0);
//		view.getProgressBar().setVisible(true);
		currentProgress = 0;
		
		for (int i = 0; i < MAX_NUMBER_OF_CONCURRENTLY_RUNNING_REQUESTS; i++) {
			sendNewLikeImportRequest(allFriends);
			currentNumberOfActiveRequests++;
		}
	}
	
	private void sendNewLikeImportRequest(final FacebookFriend[] allFriends) {
		
		int startIndex = currentFriendIndex;
		int endIndex = currentFriendIndex + NUMBER_OF_IMPORTED_FRIENDS_PER_REQUEST;
		
		if (endIndex > allFriends.length) {
			endIndex = allFriends.length;
		}
		currentFriendIndex = endIndex;
		
		FacebookFriend[] friendsToImport = new FacebookFriend[endIndex - startIndex];
		
		for (int i = 0; i < friendsToImport.length; i++) {
			friendsToImport[i] = allFriends[startIndex + i];
		}
		
		rpcService.importLikeData(friendsToImport, accessToken, new AsyncCallback<Void>() {
			@Override
			public void onSuccess(Void result) {
				
				currentProgress += NUMBER_OF_IMPORTED_FRIENDS_PER_REQUEST;
				view.getProgressBar().setProgress(currentProgress);
				
				// if the current request is the last active one
				if (currentFriendIndex == allFriends.length && currentNumberOfActiveRequests == 1) {
					prepareClustering(allFriends);
				} else if (currentFriendIndex < allFriends.length) {
					sendNewLikeImportRequest(allFriends);
				} else {
					// if there are no more friends to import, but the current
					// request is not the last active one
					currentNumberOfActiveRequests--;
				}
			}
			
			@Override
			public void onFailure(Throwable caught) {
				
				currentProgress += NUMBER_OF_IMPORTED_FRIENDS_PER_REQUEST;
				view.getProgressBar().setProgress(currentProgress);
				
				currentNumberOfActiveRequests--;
				Window.alert("A like import request failed!");
			}
		});
	}
	
	private void prepareClustering(FacebookFriend[] allFriends) {
		
//		view.getProgressBar().setVisible(false);
//		view.getStatusLabel().setText("");
//		view.getLoadingImage().setVisible(true);
		
		view.getStatusPanel().remove(view.getProgressBar());
		view.getStatusPanel().remove(view.getStatusLabel());
		view.getLoadingImage().setVisible(true);
		
		rpcService.prepareClustering(allFriends, new AsyncCallback<List<Clusterable>>() {
			@Override
			public void onSuccess(List<Clusterable> clusterElements) {
				eventBus.fireEvent(new ReadyForClusteringEvent(clusterElements));
//				container.remove(view.asWidget());
			}
			
			@Override
			public void onFailure(Throwable caught) {
				eventBus.fireEvent(new ActionFailedEvent("Preparing clustering failed!"));
			}
		});
	}

}
